/*
 * Copyright(C) 2009, kesalin All rights reserved.
 * 
 * File name : UThreading.cpp
 * Brief     : Wrapper for Critical Section, Mutex, Event and Thread.
 * Version   : 1.0
 * Author    : kesalin@gmail.com
 * Date      : 2009-6-5
 */

#include "StdAfx.h"

#include "UThreading.h"

#include "UException.h"

namespace LuoUtils {

// Thread

Thread::Thread() :
	m_pRunnable(0),
	m_hThread(0),
	m_idThread(0),
	m_Code(-1)
{
}

Thread::~Thread()
{
	Close();
}

void Thread::Create(URunnable* run)
{
	if (m_hThread != 0) {
		LogicalError::Throw(L"unexpected");
	}

	HRESULT hRslt = m_ReadyEvent.Create(true);
	if (FAILED(hRslt)) {
		WinError::Throw(hRslt, L"ReadyEvent.Create");
	}

	m_pRunnable = run;

	unsigned tid = 0;
	uintptr_t th = _beginthreadex(
		0,
		0,
		ThreadProc,
		this,
		0,
		&tid);
	if (th == 0) {
		CRTError::Throw(L"_beginthreadex");
	}

	HANDLE aEvents[2] = {
		(HANDLE)th,
		m_ReadyEvent
	};

	DWORD result = WaitForMultipleObjects(
		sizeof(aEvents) / sizeof(aEvents[0]),
		aEvents,
		FALSE,
		60 * 1000);
	if (result == WAIT_OBJECT_0) {
		ATLTRACE("LuoUtils::Thread::OnThread() Unexpected Thread Exit. (0x%x)\n", tid);
		WinError::Throw(E_OUTOFMEMORY, L"CreateThread");

	} else if (result != WAIT_OBJECT_0 + 1) {
		ATLTRACE("LuoUtils::Thread::OnThread() Thread Initiate Error. (0x%x)\n", tid);
		WinError::Throw(E_FAIL, L"CreateThread");
	}

	m_hThread  = (HANDLE)th;
	m_idThread = (DWORD)tid;
}

void Thread::Close()
{
	Join();

	if (m_hThread != 0) {
		CloseHandle(m_hThread);

		m_pRunnable = 0;

		m_hThread  = 0;
		m_idThread = 0;
	}

	m_ReadyEvent.Close();
}

int Thread::Join()
{
	DWORD code = DWORD(-1);

	if (m_hThread != 0) {
		WaitForSingleObject(m_hThread, INFINITE);

		GetExitCodeThread(m_hThread, &code);

		m_Code = code;
	}

	return (int)code;
}

/* */

static void SetupThreadName(const char* p)
{
	INT_PTR para[4];

	para[0] = 0x1000;
	para[1] = (INT_PTR)p;
	para[2] = -1;
	para[3] = 0;

	__try {
		RaiseException(
			0x406D1388,
			0,
			sizeof(para) / sizeof(ULONG_PTR),
			(const ULONG_PTR*)para);

	} __except(EXCEPTION_CONTINUE_EXECUTION) {

	}
}

/* */

unsigned STDCALL Thread::ThreadProc(void* p)
{
	Thread* pThis = static_cast<Thread*>(p);

	unsigned code = pThis->OnThread();

	return code;
}

unsigned Thread::OnThread()
{
	if (!m_Name.IsEmpty()) {
		SetupThreadName(m_Name);
	}

	int code = -1;

	try {
		m_ReadyEvent.SetEvent();

		if (m_pRunnable != 0) {
			code = m_pRunnable->Run();
		}

	} catch (Exception&) {
		ATLTRACE("LuoUtils::Thread::OnThread() Exception.\n");
		code = -1;
	}

	return (unsigned)code;
}

/* */

} // namespace LuoUtils

